﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.Entities.Relations.DirectAssociations
{
    [Serializable]
    public class EntityLinkageInstruction:InstructionBase
    {
        #region props
        private DataProviderType _ParentDbProvider;
        /// <summary></summary>
        public DataProviderType ParentDbProvider
        {
            get
            {
                return this._ParentDbProvider;
            }
            set
            {
                this._ParentDbProvider = value;
            }
        }

        private string _ParentConnStr;
        /// <summary></summary>
        public string ParentConnStr
        {
            get
            {
                return this._ParentConnStr;
            }
            set
            {
                this._ParentConnStr = value;
            }
        }

        private string _ParentSql;
        /// <summary></summary>
        public string ParentSql
        {
            get
            {
                return this._ParentSql;
            }
            set
            {
                this._ParentSql = value;
            }
        }

        private string _ParentKeyFieldName;
        /// <summary></summary>
        public string ParentKeyFieldName
        {
            get
            {
                return this._ParentKeyFieldName;
            }
            set
            {
                this._ParentKeyFieldName = value;
            }
        }

        private DataProviderType _ChildDbProvider;
        /// <summary></summary>
        public DataProviderType ChildDbProvider
        {
            get
            {
                return this._ChildDbProvider;
            }
            set
            {
                this._ChildDbProvider = value;
            }
        }

        private string _ChildConnStr;
        /// <summary></summary>
        public string ChildConnStr
        {
            get
            {
                return this._ChildConnStr;
            }
            set
            {
                this._ChildConnStr = value;
            }
        }

        private string _ChildSql;
        /// <summary></summary>
        public string ChildSql
        {
            get
            {
                return this._ChildSql;
            }
            set
            {
                this._ChildSql = value;
            }
        }

        private string _ChildPKFieldName;
        /// <summary></summary>
        public string ChildPKFieldName
        {
            get
            {
                return this._ChildPKFieldName;
            }
            set
            {
                this._ChildPKFieldName = value;
            }
        }

        private string _ChildFKFieldName;
        /// <summary></summary>
        public string ChildFKFieldName
        {
            get
            {
                return this._ChildFKFieldName;
            }
            set
            {
                this._ChildFKFieldName = value;
            }
        }

        private EntityReferenceType _ReferenceType;
        /// <summary></summary>
        public EntityReferenceType ReferenceType
        {
            get
            {
                return this._ReferenceType;
            }
            set
            {
                this._ReferenceType = value;
            }
        }

        private double _HopFactor;
        /// <summary>
        /// default to 0.25
        /// similarity=Mth.Pow(1-hopfactor, hopCount)
        /// </summary>
        public double HopFactor
        {
            get
            {
                return this._HopFactor;
            }
            set
            {
                this._HopFactor = value;
            }
        }

        private double _LinkageSimilarityThreshold;
        /// <summary>
        /// linkage distance is scaled to 2 and normalized to 1
        /// similarity = 1.0/(hopCount/2+1)
        /// </summary>
        public double LinkageSimilarityThreshold
        {
            get
            {
                return this._LinkageSimilarityThreshold;
            }
            set
            {
                this._LinkageSimilarityThreshold = value;
            }
        }

        private DataProviderType _TgtDbProvider;
        /// <summary></summary>
        public DataProviderType TgtDbProvider
        {
            get
            {
                return this._TgtDbProvider;
            }
            set
            {
                this._TgtDbProvider = value;
            }
        }

        private string _TgtConnStr;
        /// <summary></summary>
        public string TgtConnStr
        {
            get
            {
                return this._TgtConnStr;
            }
            set
            {
                this._TgtConnStr = value;
            }
        }

        private string _TgtTable;
        /// <summary></summary>
        public string TgtTable
        {
            get
            {
                return this._TgtTable;
            }
            set
            {
                this._TgtTable = value;
            }
        }

        private string _TgtSrcKeyField;
        /// <summary></summary>
        public string TgtSrcKeyField
        {
            get
            {
                return this._TgtSrcKeyField;
            }
            set
            {
                this._TgtSrcKeyField = value;
            }
        }

        private string _TgtRefKeyField;
        /// <summary></summary>
        public string TgtRefKeyField
        {
            get
            {
                return this._TgtRefKeyField;
            }
            set
            {
                this._TgtRefKeyField = value;
            }
        }

        private string _TgtLinkScoreField;
        /// <summary></summary>
        public string TgtLinkScoreField
        {
            get
            {
                return this._TgtLinkScoreField;
            }
            set
            {
                this._TgtLinkScoreField = value;
            }
        }
        #endregion

        #region ctor
        public EntityLinkageInstruction():base(Guid.Empty,Guid.Empty)
        {
            this._ParentDbProvider = DataProviderType.MSSQL;
            this._ParentConnStr = string.Empty;
            this._ParentSql = string.Empty;
            this._ParentKeyFieldName = string.Empty;
            this._ChildDbProvider = DataProviderType.MSSQL;
            this._ChildConnStr = string.Empty;
            this._ChildSql = string.Empty;
            this._ChildPKFieldName = string.Empty;
            this._ChildFKFieldName = string.Empty;
            
            this._ReferenceType = EntityReferenceType.CommonParent;
            this._HopFactor = 0.25;
            this._LinkageSimilarityThreshold = 0.5;

            this._TgtDbProvider = DataProviderType.MSSQL;
            this._TgtConnStr = string.Empty;
            this._TgtTable = string.Empty;
            this._TgtSrcKeyField = string.Empty;
            this._TgtRefKeyField = string.Empty;
            this._TgtLinkScoreField = string.Empty;
        }

        public EntityLinkageInstruction(Guid componentID, Guid jobTicketID,
            DataProviderType parentDbProvider,
            string parentConnStr,
            string parentSql,
            string parentKey,
            DataProviderType childDbProvider,
            string childConnStr,
            string childSql,
            string childPK,
            string childFK,
            EntityReferenceType refType, 
            double hopFactor,
            double linkageThreshold,
            DataProviderType tgtDbProvider,
            string tgtConnStr,
            string tgtTable, 
            string tgtSrcKeyField,
            string tgtRefKeyField,
            string tgtLinkScoreField) 
            : base(componentID, jobTicketID)
        {
            this._ParentDbProvider = parentDbProvider;
            this._ParentConnStr = parentConnStr;
            this._ParentSql = parentSql;
            this._ParentKeyFieldName = parentKey;
            this._ChildDbProvider = childDbProvider;
            this._ChildConnStr = childConnStr;
            this._ChildSql = childSql;
            this._ChildPKFieldName = childPK;
            this._ChildFKFieldName = childFK;
            
            this._ReferenceType = refType;
            this._HopFactor = hopFactor;
            this._LinkageSimilarityThreshold = linkageThreshold;

            this._TgtDbProvider = tgtDbProvider;
            this._TgtConnStr = tgtConnStr;
            this._TgtTable = tgtTable;
            this._TgtSrcKeyField = tgtSrcKeyField;
            this._TgtRefKeyField = tgtRefKeyField;
            this._TgtLinkScoreField = tgtLinkScoreField;
        }
        #endregion

        public override InstructionBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                EntityLinkageInstruction instruction = new EntityLinkageInstruction();
                instruction.OwnerComponentID = new Guid(xNode.Attributes.GetNamedItem("ComponentID").Value);
                instruction.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
                instruction.SubmitTime = DateTime.Parse(xNode.Attributes.GetNamedItem("SubmitTime").Value);
                instruction.StartTime = DateTime.Parse(xNode.Attributes.GetNamedItem("StartTime").Value);

                
                // parent entity 
                instruction.ParentDbProvider = (DataProviderType)
                                            int.Parse(XmlDataUtil.GetAttributeValue(xNode, "ParentDbProvider",
                                                                                    ((int) DataProviderType.MSSQL).
                                                                                        ToString()));
                instruction.ParentConnStr = XmlDataUtil.GetAttributeValue(xNode, "ParentConnStr", "");
                instruction.ParentSql = XmlDataUtil.GetAttributeValue(xNode, "ParentSql", "");
                instruction.ParentKeyFieldName = XmlDataUtil.GetAttributeValue(xNode, "ParentKeyFieldName", "");
                
                // child entity
                instruction.ChildDbProvider =
                    (DataProviderType) int.Parse(XmlDataUtil.GetAttributeValue(
                                                     xNode, "ChildDbProvider", ((int) DataProviderType.MSSQL).ToString()));
                instruction.ChildConnStr = XmlDataUtil.GetAttributeValue(xNode, "ChildConnStr", "");
                instruction.ChildSql = XmlDataUtil.GetAttributeValue(xNode, "ChildSql", "");
                instruction.ChildPKFieldName = XmlDataUtil.GetAttributeValue(xNode, "ChildPKFieldName", "");
                instruction.ChildFKFieldName = XmlDataUtil.GetAttributeValue(xNode, "ChildFKFieldName", "");
                
                // ref type
                instruction.ReferenceType =
                    (EntityReferenceType)
                    int.Parse(XmlDataUtil.GetAttributeValue(
                                  xNode, "RefType",
                                  ((int) EntityReferenceType.CommonParent).ToString()));

                // score
                instruction.HopFactor = double.Parse(XmlDataUtil.GetAttributeValue(xNode, "HopFactor", "0.25"));
                instruction.LinkageSimilarityThreshold = double.Parse(
                    XmlDataUtil.GetAttributeValue(xNode, "LinkageThreshold", "0.25"));

                // output 
                instruction.TgtDbProvider =
                    (DataProviderType)
                    int.Parse(
                        XmlDataUtil.GetAttributeValue(xNode, "TgtDbProvider", ((int) DataProviderType.MSSQL).ToString()));
                instruction.TgtConnStr = XmlDataUtil.GetAttributeValue(xNode, "TgtConnStr", "");
                instruction.TgtTable = XmlDataUtil.GetAttributeValue(xNode, "TgtTable", "");
                instruction.TgtSrcKeyField = XmlDataUtil.GetAttributeValue(xNode, "TgtSrcKeyField", "");
                instruction.TgtRefKeyField = XmlDataUtil.GetAttributeValue(xNode, "TgtRefKeyField", "");
                instruction.TgtLinkScoreField = XmlDataUtil.GetAttributeValue(xNode, "TgtLinkScoreField", "");

                return instruction;
            }
            else
            {
                throw new Exception("Unable to instantiate instruction: invalid type specified in xml node");
            }
        }

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Instruction");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComponentID", this.OwnerComponentID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SubmitTime", this.SubmitTime.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "StartTime", this.StartTime.ToString());

            // parent meta data
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ParentDbProvider", ((int) this.ParentDbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ParentConnStr", this.ParentConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ParentSql", this.ParentSql);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ParentKeyFieldName", this.ParentKeyFieldName);

            // child meta data 
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ChildDbProvider", ((int) this.ChildDbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ChildConnStr", this.ChildConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ChildSql", this.ChildSql);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ChildPKFieldName", this.ChildPKFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ChildFKFieldName", this.ChildFKFieldName);

            // ref type
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "RefType", ((int) this.ReferenceType).ToString());

            // score
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "HopFactor", this.HopFactor.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "LinkageThreshold", this.LinkageSimilarityThreshold.ToString());

            // output 
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtDbProvider", ((int) this.TgtDbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtConnStr", this.TgtConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtTable", this.TgtTable);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtSrcKeyField", this.TgtSrcKeyField);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtRefKeyField", this.TgtRefKeyField);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtLinkScoreField", this.TgtLinkScoreField);

            return xNode;
        }
    }
}
